#include <iostream>
#include <functional>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <unordered_map>
#include <sys/socket.h>

#include "Log.hpp"

using namespace std;

typedef function<string(const string &, const string &, uint16_t)> func_;

enum
{
    Sockst_Err = 1,
    Bind_Err
};

uint16_t defalutport = 8080;
string defalutip = "0.0.0.0";

class UdpServer
{
public:
    UdpServer(const uint16_t &port = defalutport, const string &ip = defalutip)
        : sockfd_(0), ip_(ip), port_(port), Rurnning_(false)
    {
    }

    void Init()
    {
        sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (sockfd_ < 0)
        {
            log(Fatal, "socket create error, sockfd: %d: ", sockfd_);
            exit(Sockst_Err);
        }
        log(Info, "socket create success, sockfd: %d: ", sockfd_);

        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        local.sin_addr.s_addr = inet_addr(ip_.c_str());

        if (bind(sockfd_, (const struct sockaddr *)&local, sizeof(local)) < 0)
        {
            log(Fatal, "socket bind error, error: %d: , err string: %s", errno, strerror(errno));
            exit(Bind_Err);
        }
        log(Info, "socket bind success, error: %d: , err string: %s", errno, strerror(errno));
    }

    // 插入创建sockaddr到online_user_的map里面
    void checkUse(const struct sockaddr_in& client, const string& clientip, const uint16_t clientport)
    {
        auto iter = online_user_.find(clientip);
        if(iter == online_user_.end())
        {
            online_user_.insert({clientip, client});
            cout << "[" << clientip << ":" << clientport << "] add to online user." << endl;
        }
    }

    //发送信息
    void Boardcast(const string& info, const string& clientip, const uint16_t clientport)
    {
        for(const auto& user : online_user_)
        {
            string message = "[";
            message += clientip;
            message += ":";
            message += to_string(clientport);
            message += "]#";
            message += info;
            socklen_t len = sizeof(user.second);
            sendto(sockfd_, message.c_str(), sizeof(message), 0, (struct sockaddr*)(&user.second), len);
        }
    }

    void Run()
    {
        Rurnning_ = true;
        char buffer[1024];
        while(Rurnning_)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            //收消息
            ssize_t n = recvfrom(sockfd_, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&client, &len);
            if(n < 0)
            {
                log(Warning, "recvfrom error, error %d, err string: %s: ", errno, strerror(errno));
                continue;
            }

            uint16_t clientport = ntohs(client.sin_port);
            string clientip =  inet_ntoa(client.sin_addr);
            checkUse(client, clientip, clientport);

            string info = buffer;
            Boardcast(info, clientip, clientport);
        }
    }

    ~UdpServer()
    {
        if (sockfd_ > 0)
        {
            close(sockfd_);
        }
    }

private:
    int sockfd_;
    string ip_;
    uint16_t port_;
    bool Rurnning_;
    unordered_map<string, struct sockaddr_in> online_user_;
};